3장 컴포넌트


컴포넌트 선언하는 방법 2가지임.

  1. 함수 컴포넌트
  2. 클래스형 컴포넌트

클래스형 컴포넌트는 state 랑 사이클 기능 사용할 수 있고 임의 메서드 정의할 수 있음.
새로운 MyComponent.js 파일 만들어줌

const MyComponent = () => {
  return <div>나의 새롭고 멋진 컴포넌트</div>;
};

export default MyComponent;

export default MyComponent 는 다른 파일에서 이 파일을 import 할 때, MyComponent 클래스를 불러오도록 설정.

props

컴포넌트 속성을 설정할 때 사용하는 요소
props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있음.
props 값은 컴포넌트 함수의 파라미터로 받아와서 사용할 수 있음.

const MyComponent = (props) => {
  return <div>안녕하세요, 제 이름은 {props.name} 입니다.</div>;
};

export default MyComponent;

부모 컴포넌트에서 props 값 지정.

const App = () => {
  return <MyComponent name="react" />;
};

export default App;

아마도 부모 컴포넌트가 인자로 주는 모든 값들이 props라는 객체의 프로퍼티가 되는 것 같음.

props 기본값 설정 : defaultProps

const MyComponent = (props) => {
  return <div>안녕하세요, 제 이름은 {props.name} 입니다.</div>;
};
MyComponent.defaultProps = {
  name: "기본 이름",
};

아마 props 객체에서 해당 프로퍼티가 없을 경우 defaultProps 객체에서 default값을 가져와서 사용하나 봄.

태그 사이 내용을 보여주는 children

props의 children 프로퍼티는 컴포넌트 태그 사이의 내용을 보여준다.

//App.js
const App = () => {
  return <MyComponent>리액트</MyComponent>;
};
//MyComponent.js
const MyComponent = (props) => {
  return (
    <div>
      안녕하세요, 제 이름은 {props.name} 입니다.
      <br />
      children 값은 {props.children} 입니다.
    </div>
  );
};

비구조화 할당 문법으로 props 내부값 추출

객체 형태로 props 내부값 한번에 받아오기로 보면됨.

const MyComponent = ({ name, children }) => {
  return (
    <div>
      안녕하세요, 제 이름은 {name} 입니다.
      <br />
      children 값은 {children} 입니다.
    </div>
  );
};

propTypes를 통한 props 검증

ProTypes 이용해서 props 의 type을 지정할 수 있음.

import PropTypes from "prop-types";
MyComponent.propTypes = {
  name: PropTypes.string,
};

type 다르면 화면에 출력하긴 하지만, 오류 메시지 띄워줌.
➕ isRequired 로 필수 지정.

MyComponent.propTypes = {
  name: PropTypes.string,
  favoriteNumber: PropTypes.number.isRequired,
};

클래스형 컴포넌트에서 props 사용하기

this.props으로 조회하면 됨.

class MyComponent extends Component {
  render() {
    const { name, favoriteNumber, children } = this.props; // 비구조화 할당
    return (
      <div>
        안녕하세요, 제 이름은 {name}입니다. <br />
        children 값은 {children}
        입니다.
        <br />
        제가 좋아하는 숫자는 {favoriteNumber}입니다.
      </div>
    );
  }
}

defaultProps랑 propTypes class 정적 필드으로도 지정가능

class MyComponent extends Component {
  static defaultProps = {
    name: '기본 이름'
  };
  static propTypes = {
    name: PropTypes.string,
    favoriteNumber: PropTypes.number.isRequired
  };
  render() {
    const { name, favoriteNumber, children } = this.props; // 비구조화 할당
    return (...);
  }
}

state

state는 컴포넌트 내부에서 바뀔 수 있는 값이다.
props는 부모 컴포넌트가 설정하는 값으로 자식 컴포넌트는 읽기 전용으로만 사용지만 state를 사용해서 컴포넌트 내부에서 변수 처럼? 사용가능.

클래스형 컴포넌트는 state를 함수 컴포넌트에서는 useState라는 함수로 구현

클래스형 컴포넌트의 state

import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 0,
    };
  }

  render() {
    const { number } = this.state;
    return (
      <div>
        <h1>{number}</h1>
        <button
          onClick={() => {
            this.setState({ number: number + 1 });
          }}
        >
          +1
        </button>
      </div>
    );
  }
}
export default Counter;

클래스형 컴포넌트는 Component를 상속받으므로 constructor 에서 super 호출해주고, state 선언
state는 객체 형식이어야 한다.
this.setState 함수를 통해서 객체 형식으로 state 값을 넘겨 주면 최신화 시켜준다.
state의 모든 프로퍼티를 포함하지 않고 변경할 프로퍼티만 설정해 주면 된다.
➕ onClick에서 이벤트 등록할 때, 화살표 함수로 정의해줘야 this.setState 를 사용할 수 있다. 일반함수로 선언하면 전역객체를 가르킨다.

클래스 필드에서 state를 선언해도된다.

import React, { Component } from 'react';
 
class Counter extends Component {
  state = {
    number: 0,
    fixedNumber: 0
  };
  render() {
    const { number, fixedNumber } = this.state; // state를 조회할 때는 this.state로 조회합니다.
    return (...);
  }
}
 
export default Counter;

this.setState에 객체 대신 함수 인자 전달하기

this.propsthis.state는 모두 렌더링된 값을 나타낸다.
this.setState 사용해서 state값 업데이트할 때 상태가 비동기적으로 업데이트 됨.
즉, setState 호출 직후 새로운 값이 this.state에 반영되지 않음.

onClick={() => {
  // this.setState를 사용하여 state에 새로운 값을 넣을 수 있습니다.
  this.setState({ number: number + 1 });
  this.setState({ number: this.state.number + 1 });
}}
incrementCount() {
  // 주의: 이 코드는 예상대로 동작하지 *않을 것*입니다.
  this.setState({count: this.state.count + 1});
}

handleSomething() {
  // `this.state.count`가 0에서 시작한다고 해봅시다.
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();
  // React가 컴포넌트를 리렌더링할 때 `this.state.count`는 3이 될 것 같은 예상과 달리 1이 됩니다.

  // 이것은 `incrementCount()` 함수가 `this.state.count`에서 값을 읽어 오는데
  // React는 컴포넌트가 리렌더링될 때까지 `this.state.count`를 갱신하지 않기 때문입니다.
  // 그러므로 `incrementCount()`는 매번 `this.state.count`의 값을 0으로 읽은 뒤에 이 값을 1로 설정합니다.

  // 이 문제의 해결 방법은 아래에 설명되어 있습니다.
}

그래서 위 코드들 실행해도 숫자가 1씩 더해짐.

❓ 어떻게 렌더링되지않은 최신의 state 값을 가져올 수 있을까?
setState에 객체 대신 함수를 인자로 넣어주면 현재 state값과 props를 인자로 호출됨.

this.setState((prevState, props) => {
return {
  // 업데이트하고 싶은 내용
}
})

이를 이용해서 작성하면 최신의 state 값을 가져올 수 있음.

onClick={() => {
            this.setState({ number: ++number });
            this.setState((prevState) => {
              return { number: prevState.number + 1 };
            });
          }}

this.setState가 끝난 후 특정 작업 실행하기

setState의 두번째 인자로 callback 함수를 등록하면, state값이 업데이트 된 뒤에 callback 함수가 실행됨.

<button
  // onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
  onClick={() => {
    this.setState(
      {
        number: number + 1
      },
      () => {
        console.log('방금 setState가 호출되었습니다.');
        console.log(this.state);
      }
    );
  }}
>
  +1
</button>

함수컴포넌트에서 useState 사용하기

const Say = () => {
  const [message, setMessage] = useState("");// 초기값 설정
		//message 는 현재 상태, setMessage message의 상태를 바꾸어주는 세터 함수
  const onClickEnter = () => setMessage("안녕하세요!");
  const onClickLeave = () => setMessage("안녕히가세요1");

  const [color, setColor] = useState("black");

  return (
    <div>
      <button onClick={onClickEnter}>입장</button>
      <button onClick={onClickLeave}>퇴장</button>
      <h1 style={{ color }}>
        {message}
        {color}
      </h1>

      <button style={{ color: "red" }} onClick={() => setColor("red")}>
        빨간색
      </button>
      <button style={{ color: "green" }} onClick={() => setColor("green")}>
        초록색
      </button>
      <button style={{ color: "blue" }} onClick={() => setColor("blue")}>
        파란색
      </button>
    </div>
  );
};

State 사용할 때 주의 사항

state 값을 바꿀 때는 setState 혹은 useState 통해 전달받은 세터 함수를 사용해야 함.
❓ 객체나 배열 업데이트 할 때는 어떻게 함?
⇒ 스프레드 문법 이용해서 덮어씌우셈.